/**
 * 
 */
package tau.adx.common.publishers;

import java.util.Map;

import lombok.Data;
import lombok.EqualsAndHashCode;
import tau.adx.common.ads.properties.AdAttributeProbabilityMaps;
import tau.adx.common.ads.properties.AdType;
import tau.adx.common.bids.BidInfo;
import tau.adx.common.devices.Device;
import tau.adx.common.publishers.reserve.ReservePriceManager;
import tau.adx.common.users.AdxUser;
import tau.adx.common.users.properties.AdxUserAttributeProbabilityMaps;
import tau.adx.simulator.AdX;

/**
 * Defines a publisher in the Ad-Exchange system.<br>
 * A publisher is defined by the following properties: <li>
 * {@link AdxUserAttributeProbabilityMaps Probability maps} to define how likely
 * a user with a certain attribute is to visit the {@link Publisher}.</li> <li>
 * A probability {@link Map} of how likely an ad of type {@link AdType} is to be
 * shown by this {@link Publisher}.</li> <li>
 * The relative popularity of web site w, i.e., the probability of an arbitrary
 * user visiting the web site.</li> <li>{@link Double pImpressions} Every user
 * visit to a web site results in one or more impression (i.e., ad opportunities
 * ) according to a predefined probability distribution.</li> <li>
 * The {@link AdxPublisher}'s {@link ReservePriceManager}.</li>
 * 
 * @author greenwald
 * 
 */
@Data
@EqualsAndHashCode(exclude = "reservePriceManager")
public class AdxPublisher {

	/**
	 * A probability {@link Map} of how likely an ad of type {@link AdType} is
	 * to be shown by this {@link Publisher}.
	 */
	private Map<AdType, Double> adTypeDistribution;

	/**
	 * A probability {@link Map} of how likely a {@link AdxUser user} with
	 * {@link Device} is to visit this {@link Publisher}.
	 */
	private Map<Device, Double> deviceProbabilityMap;

	/**
	 * {@link AdxPublisher Publisher's} name.
	 */
	private String name;

	/**
	 * Every user visit to a web site results in one or more impression (i.e.,
	 * ad opportunities ) according to a predefined probability distribution.
	 */
	private double pImpressions;

	/**
	 * {@link AdxUserAttributeProbabilityMaps Probability maps} to define how
	 * likely a user with a certain attribute is to visit the {@link Publisher}.
	 */
	private AdxUserAttributeProbabilityMaps probabilityMaps;

	/**
	 * The relative popularity of web site w, i.e., the probability of an
	 * arbitrary user visiting the web site.
	 */
	private double relativePopularity;

	/**
	 * The {@link AdxPublisher}'s {@link ReservePriceManager}.
	 */
	private ReservePriceManager reservePriceManager;

	/**
	 * @param probabilityMaps
	 *            {@link AdxUserAttributeProbabilityMaps} to define how likely a
	 *            user with a certain attribute is to visit the
	 *            {@link Publisher}.
	 * @param adAttributeProbabilityMaps
	 *            {@link AdAttributeProbabilityMaps} of how likely an ad of type
	 *            {@link AdType} is to be shown by this {@link Publisher}.
	 * @param deviceProbabilityMap
	 *            A probability {@link Map} of how likely a {@link AdxUser user}
	 *            with {@link Device} is to visit this {@link Publisher}.
	 * @param relativePopularity
	 *            The relative popularity of web site w, i.e., the probability
	 *            of an arbitrary user visiting the web site.
	 * @param pImpressions
	 *            Every user visit to a web site results in one or more
	 *            impression (i.e., ad opportunities ) according to a predefined
	 *            probability distribution.
	 * @param reservePriceManager
	 *            The {@link AdxPublisher}'s {@link ReservePriceManager}.
	 * @param name
	 *            {@link AdxPublisher Publisher's} name.
	 */
	public AdxPublisher(AdxUserAttributeProbabilityMaps probabilityMaps,
			AdAttributeProbabilityMaps adAttributeProbabilityMaps,
			Map<Device, Double> deviceProbabilityMap,
			double relativePopularity, double pImpressions,
			ReservePriceManager reservePriceManager, String name) {
		this.probabilityMaps = probabilityMaps;
		this.adTypeDistribution = adAttributeProbabilityMaps
				.getAdTypeDistribution();
		this.relativePopularity = relativePopularity;
		this.pImpressions = pImpressions;
		this.reservePriceManager = reservePriceManager;
		this.deviceProbabilityMap = deviceProbabilityMap;
		this.name = name;
	}

	/**
	 * Handles {@link AdxUser user} visit to this {@link AdxPublisher publisher}
	 * .
	 * 
	 * @param adxUser
	 *            {@link AdxUser} to handle visit for.
	 * @param device
	 *            The {@link Device} used for the visit.
	 */
	public void handleUserVisit(AdxUser adxUser, Device device) {
		double reservePrice = reservePriceManager.generateReservePrice();
		BidInfo bidInfo = AdX.getInstance().runAuction(adxUser, this,
				reservePrice);
		if (bidInfo != null) {
			reservePriceManager.addImpressionForPrice(reservePrice);
		}
	}

	/**
	 * Determines how likely a {@link AdxUser user} is to visit this
	 * {@link AdxPublisher publisher} with a {@link Device}.
	 * 
	 * @param user
	 *            {@link AdxUser}.
	 * @param device
	 *            {@link Device}.
	 * @return How likely the {@link AdxUser user} is to visit this
	 *         {@link AdxPublisher publisher} with {@link Device device}.
	 */
	public double willVisit(AdxUser user, Device device) {
		double d = probabilityMaps.getAgeDistribution().get(user.getAge())
				* probabilityMaps.getGenderDistribution().get(user.getGender())
				* probabilityMaps.getIncomeDistribution().get(user.getIncome())
				* deviceProbabilityMap.get(device) * relativePopularity
				/ AdX.getUserPopularity(user) / AdX.getDevicePopularity(device);
		return d;
	}
}
